package com.xbongbong.paas.service.impl;

import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.pojo.dto.SearchDataInConditionDTO;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.LinkInfoPojo;
import com.xbongbong.paas.field.RelyConditionPoJo;
import com.xbongbong.paas.field.RelyPoJo;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.paas.pojo.ItemDataPoJo;
import com.xbongbong.paas.pojo.dto.UserAndDepartmentGetDTO;
import com.xbongbong.paas.pojo.linkdata.DataLinkagePoJo;
import com.xbongbong.paas.pojo.linkdata.DataLinkageViewPoJo;
import com.xbongbong.paas.pojo.linkdata.dto.DataLinkageDTO;
import com.xbongbong.paas.pojo.linkdata.vo.DataLinkageVO;
import com.xbongbong.paas.service.DistinctDataService;
import com.xbongbong.paas.service.LinkRelyDataService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.thread.TransmittableThreadLocalUtil;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.util.DataUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.parent.condition.ProConditionHelp;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.packages.enums.FeaturesEnum;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.ListGroupEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.help.SaasFormHelp;
import com.xbongbong.saas.help.SaasListHelp;
import com.xbongbong.saas.model.IndexTypeModel;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * @author 吴峰
 * @date 2022/03/24 11:28
 */
@Service("linkRelyDataService")
public class LinkRelyDataServiceImpl implements LinkRelyDataService {

    private static Logger log = LoggerFactory.getLogger(LinkRelyDataServiceImpl.class);

    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private SaasListHelp saasListHelp;
    @Resource
    private ProConditionHelp proConditionHelp;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private DistinctDataService distinctDataService;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private SaasFormHelp saasFormHelp;

    @Override
    public DataLinkageVO linkage(DataLinkageDTO linkDataDTO) throws XbbException {
        DataLinkageVO linkDataVO = new DataLinkageVO();
        try {
            String subFormAttr = linkDataDTO.getSubFormAttr();
            String corpid = linkDataDTO.getCorpid();
            List<String> featureList = packageHelp.getFeatureList(corpid);
            if (!featureList.contains(FeaturesEnum.DATARELY.getAlias())) {
                // 该公司的套餐，没有这个功能
                linkDataVO.setList(new ArrayList<>());
                return linkDataVO;
            }
            Integer businessType = linkDataDTO.getBusinessType();
            Long formId = linkDataDTO.getFormId();
            UserVO userVO = linkDataDTO.getLoginUser();
            Set<Long> formIdSet = new HashSet<>();
            Set<Long> workOrderFormIdSet = new HashSet<>();
            if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), businessType)) {
                workOrderFormIdSet.add(formId);
            } else {
                formIdSet.add(formId);
            }

            linkDataDTO.getLinkDataList().forEach(item->{
                RelyPoJo relyPoJo = item.getRely();
                Long sourceFormId = relyPoJo.getSourceFormId();
                if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), relyPoJo.getSourceBusinessType())) {
                    workOrderFormIdSet.add(sourceFormId);
                } else {
                    formIdSet.add(sourceFormId);
                }
            });
            Map<Long, Map<String, FieldAttrEntity>> formExplainMap =proFormHelp.getFormFieldMap(formIdSet, corpid);

            Map<Long, Map<String, FieldAttrEntity>> workOrderFormExplainMap =proFormHelp.getWorkOrderFormFieldMap(workOrderFormIdSet, corpid);
            // 本表单
            Map<String, FieldAttrEntity> thisExplainMap;
            if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), businessType)) {
                thisExplainMap = workOrderFormExplainMap.get(formId);
            } else {
                thisExplainMap = formExplainMap.get(formId);
            }
            if (CollectionsUtil.isNotEmpty(linkDataDTO.getPreviewFieldList())) {
                thisExplainMap = ExplainUtil.getExplainMapByList(linkDataDTO.getPreviewFieldList(), null);
            }
            Map<String, FieldAttrEntity> targetFieldMap;
            List<DataLinkageViewPoJo> list = new ArrayList<>();
            for (DataLinkagePoJo item : linkDataDTO.getLinkDataList()) {
                DataLinkageViewPoJo linkDataViewPoJo = new DataLinkageViewPoJo();
                Map<String, Object> conditionList = item.getConditionList();
                RelyPoJo relyPoJo = item.getRely();
                String linkFieldAttr = relyPoJo.getLinkField();
                if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), relyPoJo.getSourceBusinessType())) {
                    targetFieldMap = workOrderFormExplainMap.get(relyPoJo.getSourceFormId());
                } else {
                    targetFieldMap = formExplainMap.get(relyPoJo.getSourceFormId());
                }
                ExplainUtil.setDataIdField(targetFieldMap);
                FieldAttrEntity linkField = targetFieldMap.get(linkFieldAttr);
                if (Objects.isNull(linkField)) {
                    continue;
                }
                NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                SearchDataInConditionDTO searchDataInConditionDTO = new SearchDataInConditionDTO();
                searchDataInConditionDTO.setCorpid(corpid);
                searchDataInConditionDTO.setLoginUser(userVO);
                searchDataInConditionDTO.setUserId(userVO.getUserId());
                // 获取下拉框的查询条件 todo
                if (Objects.nonNull(searchDataInConditionDTO.getRelyBoolQueryBuilder())) {
                    boolQueryBuilder = searchDataInConditionDTO.getRelyBoolQueryBuilder();
                }
                //解析查询条件
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termQuery("appId", relyPoJo.getSourceAppId()));
                boolQueryBuilder.filter(termQuery("formId", relyPoJo.getSourceFormId()));
                boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));

                // 获取权限内的数据
                searchDataInConditionDTO.setDefaultGroup(1);
                searchDataInConditionDTO.setListGroupId(ListGroupEnum.ALL.getCode().longValue());
                searchDataInConditionDTO.setSaasMark(relyPoJo.getSourceSaasMark());
                searchDataInConditionDTO.setBusinessType(relyPoJo.getSourceBusinessType());
                searchDataInConditionDTO.setSubBusinessType(relyPoJo.getSourceBusinessType());
                searchDataInConditionDTO.setLoginUser(linkDataDTO.getLoginUser());
                searchDataInConditionDTO.setFormId(relyPoJo.getSourceFormId());
                TransmittableThreadLocalUtil.setValue(relyPoJo.getSourceSaasMark(), relyPoJo.getSourceBusinessType(), relyPoJo.getSourceMenuId(), null);
                saasListHelp.packageSearchDataCondition(searchDataInConditionDTO);
                IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(corpid, relyPoJo.getSourceBusinessType(), relyPoJo.getSourceSaasMark());

                EsUtil.parseCondition(boolQueryBuilder,searchDataInConditionDTO.getConditionsList(),"", corpid,relyPoJo.getSourceBusinessType());
                BoolQueryBuilder boolQuery = boolQuery();
                EsUtil.parseShouldCondition(boolQuery,searchDataInConditionDTO.getShouldConditionsList(),"",corpid,relyPoJo.getSourceBusinessType());
                boolQueryBuilder.filter(boolQuery);
                // 共享权限
                EsUtil.parseSharePermissionCondition(boolQueryBuilder, searchDataInConditionDTO.getSharePermission(), corpid, relyPoJo.getSourceBusinessType(), false);

                if (!targetFieldMap.containsKey(linkFieldAttr)) {
                    linkDataViewPoJo = new DataLinkageViewPoJo(item.getFieldList(), new ArrayList<>(), item.getUid());
                    list.add(linkDataViewPoJo);
                    continue;
                }
                Map<String, String> fieldMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (CollectionsUtil.isEmpty(relyPoJo.getCondition())) {
                    // 将老的数据格式转化成新的数据格式
                    RelyConditionPoJo conditionPoJo = new RelyConditionPoJo(relyPoJo.getDisplayField(), null, relyPoJo.getTargetField(), relyPoJo.getTargetFieldType());
                    List<RelyConditionPoJo> relyConditionPoJos = new ArrayList<>();
                    relyConditionPoJos.add(conditionPoJo);
                    relyPoJo.setCondition(relyConditionPoJos);
                }
                relyPoJo.getCondition().forEach(conditionPoJo->{
                    fieldMap.put(conditionPoJo.getDisplayField(), conditionPoJo.getTargetField());
                });
                for (Map.Entry<String, Object> entry : conditionList.entrySet()) {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    FieldAttrEntity targetFieldEntity;
                    if (CollectionsUtil.isNotEmpty(relyPoJo.getCondition())) {
                        String targetAttr = fieldMap.get(key);
                        targetFieldEntity = targetFieldMap.get(targetAttr);
                    } else {
                        targetFieldEntity = targetFieldMap.get(relyPoJo.getTargetField());
                    }
                    if (targetFieldEntity == null) {

                        continue;
                    }
                    FieldAttrEntity fieldAttrEntity;
                    if (key.contains(StringConstant.POINT)) {
                        String[] attrArray = key.split("\\.");
                        FieldAttrEntity subFormField = thisExplainMap.get(attrArray[0]);
                        Map<String, FieldAttrEntity> subFormMap = ExplainUtil.getExplainMapByList(subFormField.getSubForm().getItems());
                        fieldAttrEntity = subFormMap.get(attrArray[1]);
                    } else {
                        fieldAttrEntity = thisExplainMap.get(key);
                    }
                    if (fieldAttrEntity == null) {

                        continue;
                    }
                    proConditionHelp.validateTheForm(fieldAttrEntity, targetFieldEntity, relyPoJo.getSourceBusinessType());
                    if (proConditionHelp.isEmpty(value, fieldAttrEntity.getFieldType())) {
                        EsUtil.isNull(targetFieldEntity.getFieldType(), boolQueryBuilder, targetFieldEntity.getAttr());
                    } else {
                        if (ProConditionHelp.validateIsLinkData(fieldAttrEntity.getFieldType(), targetFieldEntity.getFieldType()) && value instanceof Map) {
                            Map<String, Object> map = (Map) ((Map) value).get(StringConstant.JSON_DATA);
                            if (map == null) {
                                continue;
                            }
                            LinkInfoPojo linkInfoPojo = fieldAttrEntity.getLinkInfo();
                            if (Objects.isNull(linkInfoPojo)) {
                                continue;
                            }
                            value = map.get(linkInfoPojo.getLinkKey());
                        }
                        if (proConditionHelp.isEmpty(value, fieldAttrEntity.getFieldType())) {
                            EsUtil.isNull(targetFieldEntity.getFieldType(), boolQueryBuilder, targetFieldEntity.getAttr());
                        } else {
                            // 目标表单需要等于的值进行拼装es
                            proConditionHelp.needEqual(value, boolQueryBuilder, relyPoJo.getSourceFormId(), relyPoJo.getSourceSaasMark(), relyPoJo.getSourceBusinessType(), targetFieldEntity, fieldAttrEntity, corpid);
                        }
                    }
                }
                boolean searchParentProduct = true;
                // 数据联动多条件，需要判断是否
                for(RelyConditionPoJo relyConditionPoJo: relyPoJo.getCondition()) {
                    String targetField = relyConditionPoJo.getTargetField();
                    FieldAttrEntity subField = null;
                    if (targetField.contains(".")) {
                        List<String> targetFieldList = ExplainUtil.getAttrByPoint(targetField);
                        List<? extends FieldAttrEntity> subFieldList = targetFieldMap.get(targetFieldList.get(0)).getSubForm().getItems();
                        for (FieldAttrEntity sub : subFieldList) {
                            if (Objects.equals(sub.getAttr(), targetFieldList.get(1))) {
                                subField = sub;
                                break;
                            }
                        }
                    } else {
                        subField = targetFieldMap.get(targetField);
                    }
                    if (Objects.isNull(subField)) {
                        continue;
                    }
                    if (proConditionHelp.validateLinkData(subField.getFieldType())) {
                        searchParentProduct = false;
                        break;
                    }
                }
                if (searchParentProduct) {
                    // 当传过来的数据不是id时，产品搜父产品的就可以
                    distinctDataService.productCondition(boolQueryBuilder,relyPoJo.getSourceBusinessType(), relyPoJo.getSourceBusinessType(), "");
                }
                // 模糊查询 类似于sql中的like
                distinctDataService.likeBoolQuery(searchDataInConditionDTO.getValue(), targetFieldMap.get(relyPoJo.getLinkField()), corpid,
                        relyPoJo.getSourceFormId(), relyPoJo.getSourceBusinessType(), relyPoJo.getSourceSaasMark(), boolQueryBuilder);

                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, indexTypeEnum.getType()));
                nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
                NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();

                // 子表单外数据联动，非下拉框字段
                SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                //设置查询条件
                sourceBuilder.query(searchQuery.getQuery());
                List<String> fieldList = new ArrayList<>();
                fieldList.add(relyPoJo.getLinkField());
                if (Objects.equals(FieldTypeEnum.STAGE_THRUSTER.getType(), linkField.getFieldType())) {
                    fieldList.add(PaasStageEnum.STAGE_PROCESS_ID.getAttr());
                }
                sourceBuilder.sort(new FieldSortBuilder(FieldTypeEnum.UPDATETIME.getAlias()).order(SortOrder.DESC));
                List<PaasFormDataEntityExt> dataList = paasEsModel.list(sourceBuilder, indexTypeEnum, fieldList, 1, 1);
                UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
                BeanUtil.copyProperties(searchDataInConditionDTO, userAndDepartmentGetDTO);
                if (Objects.equals(linkField.getSaasAttr(), ProductEnum.UNIT.getSaasAttr()) && Objects.equals(relyPoJo.getSourceBusinessType(), XbbRefTypeEnum.PRODUCT.getCode())) {
                    List<ItemPoJo> productItems = saasFormHelp.getProductItems(corpid);
                    linkField.setItems(productItems);
                }

                String thisFormRelyAttr = "";
                // 当前表单触发数据联动的字段，至于为什么是个数组，因为联动条件相同的字段前端会合并成一个数组返回
                // 通常情况下联动条件相同的话，最终联动显示的值也是一样的，所以没必要一个字段一个字段去获取联动显示值，只需要获取一次数据然后给这些字段显示就好了
                // 但是此处特别要注意的是关联数据和关联多选如果设置联动条件相同，那么不能将他们合并，因为关联数据和关联数据多选最终联动显示值的格式是不同的，关联数据是对象，关联数据多选是数组
                List<String> relyFieldList = item.getFieldList();
                if (CollectionsUtil.isNotEmpty(relyFieldList)) {
                    thisFormRelyAttr = relyFieldList.get(0);
                }
                if (StringUtil.isNotEmpty(subFormAttr)) {
                    thisFormRelyAttr = subFormAttr + StringConstant.POINT + thisFormRelyAttr;
                }
                FieldAttrEntity thisFormFieldAttr = thisExplainMap.get(thisFormRelyAttr);
                List<ItemDataPoJo> itemDataList = distinctDataService.getItemDataList(userAndDepartmentGetDTO, dataList, linkField, relyPoJo.getSourceSaasMark(), relyPoJo.getSourceBusinessType(), relyPoJo.getSourceFormId(), thisFormFieldAttr);
                itemDataList = DataUtil.filterRepetition(itemDataList);
                linkDataViewPoJo.setFieldList(item.getFieldList());
                linkDataViewPoJo.setItems(itemDataList);
                linkDataViewPoJo.setUid(item.getUid());
                list.add(linkDataViewPoJo);
            }
            linkDataVO.setList(list);
            return linkDataVO;
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            log.error("com.xbongbong.paas.service.impl.LinkRelyDataServiceImpl.linkData error", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }
}
